﻿using System;
using System.Timers;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.Threading;

/* Copyright (c) 2014
 * author: Ricky Kratochwil
 * @ date: 2014-05-21
 * @ summary: Used for all drawing and animation actions
*/

namespace Engine
{
	public static class CAnimationCore
	{
		private	static				int					s_iAnimationIntervall	= 20;						//Default 20ms
		private static				Bitmap				s_bmpWorld				= null;
		private static				Bitmap				s_bmpPlayboard			= null;
		public	static				PictureBox			s_pbPlayboard			= null;
		private static				CAnimationObj[]		s_rgoObjects			= new CAnimationObj[102];	//The game allows for a maximum of 100 animated objects + 2 Players
		private	static				int					s_iCurAnimObj			= 0;
		private static				bool				s_bGrid					= false;

		private static Thread	s_oThread				= null;

//////////////////////////////////////////////////////////////////////////////////////////////////////
////////// BASIC CORE OPERATIONS

		//Start animation thread, needed for all animations to be displayed
		//@ KR / 2014.05.21
		public static void StartPlayboardRenderer()
		{
			s_oThread			= new Thread(PlayboardRenderer);
			s_oThread.Start();
		}


		//Set/Update world image
		//@ KR / 2014.06.27
		internal static void UpdateWorld(Bitmap bmpWorld)
		{
			s_bmpWorld = new Bitmap(bmpWorld);
		}

		//Stop timer
		//@ KR / 2014.05.21
		public static void StopPlayboardRenderer()
		{
			if(s_oThread != null)
			{
				s_oThread.Abort();
				s_oThread.Join();
//				while(s_oThread.IsAlive){}
				s_oThread = null;
			}
		}

		//Clear animation core from all settings [<<IRREVERSIBLE>>]
		// - Removes all animation objects from array
		// - Only allowed if render thread is not running
		//@ KR / 2014.05.28
		public static void Clear()
		{
			if(s_oThread != null)
				return;

			if(s_pbPlayboard != null)
			{
				s_pbPlayboard.Image.Dispose();
				s_pbPlayboard.Image = null;
				s_pbPlayboard		= null;

				s_bmpWorld.Dispose();
				s_bmpWorld = null;

				s_bmpPlayboard.Dispose();
				s_bmpPlayboard = null;

				s_bGrid = false;

				for(int i = 0; i < s_iCurAnimObj; i++)
				{
					s_rgoObjects[i].Destroy();
					s_rgoObjects[i] = null;
				}
				s_iCurAnimObj	= 0;
			}
		}

		//Enable or disable grid
		//@ KR / 2014.07.08
		public static void SetGrid(bool bValue)
		{
			s_bGrid = bValue;
		}


//////////////////////////////////////////////////////////////////////////////////////////////////////
////////// THREAD

		//Render background image according to Intervall
		// - Thread safe
		//@ KR / 2014.06.27
		private static void PlayboardRenderer()
		{
			while(true)
			{
				if(s_bmpPlayboard != null)
					s_bmpPlayboard.Dispose();

				Bitmap	 bmpObject;
				Point	 ptLocation;	
				s_bmpPlayboard	= new Bitmap(s_bmpWorld);
				Graphics g		= Graphics.FromImage(s_bmpPlayboard);

				lock(s_rgoObjects)
				{
					for(int i = 0; i < s_iCurAnimObj; i++)
					{
						bmpObject	= s_rgoObjects[i].GetImage();
						ptLocation	= s_rgoObjects[i].GetPos();

						g.DrawImage(bmpObject, ptLocation);
					}
				}

				//Draw reference grid
				if(s_bGrid)
				{
					for(int iY = 0; iY < Const.GRIDS_YAXIS; iY++)
					{
						for(int iX = 0; iX < Const.GRIDS_XAXIS; iX++)
							g.DrawRectangle(new Pen(Color.Black, 1), iX*Const.GRID_DIM, iY*Const.GRID_DIM, Const.GRID_DIM, Const.GRID_DIM);			
					}
				}
				g.Dispose();
			
				//Exchange picturebox image in a thread save way
				if(s_pbPlayboard.Image != null)
					s_pbPlayboard.Invoke((MethodInvoker)delegate{s_pbPlayboard.Image.Dispose();});
				s_pbPlayboard.Invoke((MethodInvoker)delegate{s_pbPlayboard.Image = (Bitmap)s_bmpPlayboard.Clone();});
//				s_pbPlayboard.Invoke((MethodInvoker)delegate{s_pbPlayboard.Refresh();});

				Thread.Sleep(s_iAnimationIntervall);
			}
		}

//////////////////////////////////////////////////////////////////////////////////////////////////////
////////// SET OR REMOVE ANIMATION/PLAYER OBJ

		//Set new animation object
		// - Will set animation objects ID in array
		// - Thread safe
		//@ KR / 2014.05.21
		internal static void SetAnimObj(CAnimationObj oAnimObj)
		{	
			lock(s_rgoObjects)
			{
				s_rgoObjects[s_iCurAnimObj] = oAnimObj;
				s_rgoObjects[s_iCurAnimObj].SetID(s_iCurAnimObj++);
			}
		}

		//@ KR / 2014.06.24
		// - Remove ID from Animation Core
		// - Sort through list to prevent gaps
		// - Thread safe
		internal static void RemoveAnimObj(int iIndex)
		{
			lock(s_rgoObjects)
			{
				//No external access to array, thus no index out of bounds expected
				s_rgoObjects[iIndex].Destroy();
				s_rgoObjects[iIndex] = null;

				//Adjust index to represent the to be empty last pos
				s_iCurAnimObj--;

				//Sort array
				for(int i = iIndex; i < s_iCurAnimObj; i++)
				{				
					s_rgoObjects[i] = s_rgoObjects[i+1];
					s_rgoObjects[i].DecreaseID();
				}

				//Remove last, now redundant, object pointer (no need to call Destroy() since the object itself continues to be valid)
				s_rgoObjects[s_iCurAnimObj] = null;
			}
		}
	}
}
